04 - Filtracja obrazów
Przetwarzanie i Analiza Obrazów
Politechnika Poznańska, Instytut Robotyki i Inteligencji Maszynowej
Ćwiczenie laboratoryjne 4: Filtracja obrazów.
Powrót do spisu treści ćwiczeń laboratoryjnych
W tym ćwiczeniu:
Czym jest progowanie i jakie są jego rodzaje?
Jakie są podstawowe operacje morfologiczne i do czego służą?
Jak działają filtry uśredniające, medianowe i gaussowskie? Kiedy stosować każdy z nich?
Jakie są podstawowe transformacje geometryczne i jak je stosować do obrazów?
1. Cel ćwiczenia
Celem ćwiczenia jest zapoznanie się z podstawowymi technikami przetwarzania obrazów wykorzystywanymi przy przygotowaniu danych i usuwaniu zakłóceń:
- Progowanie (binaryzacja) obrazów: progowanie globalne, adaptacyjne i metodą Otsu.
- Operacje morfologiczne: erozja, dylatacja, opening, closing, gradient, top-hat, black-hat.
- Filtry wygładzające: uśredniające, medianowe, gaussowskie.
- Transformacje geometryczne: translacja, skalowanie, obrót oraz transformacje afiniczne i perspektywiczne.
Poniżej znajdują się krótkie wyjaśnienia, wskazówki i przykładowe fragmenty kodu w Pythonie z użyciem biblioteki OpenCV.
2. Progowanie
Progowanie zamienia obrazy szarości w obrazy binarne. Najpopularniejsze podejścia:
Progowanie globalne (stały próg) —
cv2.threshold(można także użyć maski binarnej bezpośrednio na obrazie korzystając z Numpy).Progowanie adaptacyjne —
cv2.adaptiveThreshold(użyteczne przy nierównomiernym oświetleniu).Metoda Otsu — automatyczny wybór progu (
cv2.THRESH_OTSU).
Przykład:
import cv2
import numpy as np
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# Globalne
_, th_global = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# Alternatywnie z maską:
mask = img > 127
th_global_mask = np.zeros_like(img)
th_global_mask[mask] = 255
# Adaptacyjne (GAUSSIAN lub MEAN)
th_adapt = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
# Otsu (po usunięciu szumu np. filtrem Gaussa)
blur = cv2.GaussianBlur(img, (5,5), 0)
_, th_otsu = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cv2.imshow('Global Thresholding', th_global)
cv2.imshow('Global Thresholding (mask)', th_global_mask)
cv2.imshow('Adaptive Thresholding', th_adapt)
cv2.imshow('Otsu Thresholding', th_otsu)
cv2.waitKey(0)
cv2.destroyAllWindows()Wskazówki:
Przy nierównomiernym oświetleniu użyj progowania adaptacyjnego.
Otsu dobrze działa, gdy histogram jest dwumodalny; przed Otsu warto wygładzić szum.
3. Operacje morfologiczne
Operacje morfologiczne działają na strukturze binarnego obrazu i wykorzystują element strukturalny (kernel) do modyfikacji kształtów obiektów:
cv2.erode— erozja (zmniejsza obiekty - białe elementy na czarnym tle).cv2.dilate— dylatacja (powiększa obiekty - białe elementy na czarnym tle).cv2.morphologyEx— umożliwia m.in.OPEN,CLOSE- otwarcie (usuwanie małych obiektów) i zamknięcie (wypełnianie małych dziur).
Przykład:
import cv2
import numpy as np
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
# Erozja i dylatacja
eroded = cv2.erode(binary, kernel, iterations=1)
dilated = cv2.dilate(binary, kernel, iterations=1)
# Opening (usuwanie małych obiektów)
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# Closing (wypełnianie małych dziur)
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
# Gradient morfologiczny (kontury)
gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel)
cv2.imshow('Eroded', eroded)
cv2.imshow('Dilated', dilated)
cv2.imshow('Opening', opening)
cv2.imshow('Closing', closing)
cv2.imshow('Gradient', gradient)
cv2.waitKey(0)
cv2.destroyAllWindows()Wskazówki:
Wybór rozmiaru i kształtu kernela wpływa bezpośrednio na efekt.
Opening usuwa drobne zakłócenia; closing zamyka małe dziury.
4. Filtry wygładzające
Filtry stosujemy do redukcji szumu i przygotowania obrazu do dalszej analizy.
Uśredniający (
cv2.blur) — prosty filtr średniej; rozmywa również krawędzie.Medianowy (
cv2.medianBlur) — bardzo skuteczny przy impulsowym szumie (salt-and-pepper).Gaussowski (
cv2.GaussianBlur) — wygładza przy zachowaniu lepszych krawędzi niż prosty średni.
Przykład:
import cv2
img = cv2.imread('image.jpg')
# Uśredniający
blur_mean = cv2.blur(img, (5,5))
# Medianowy (kernel musi być nieparzysty)
blur_median = cv2.medianBlur(img, 5)
# Gaussowski
blur_gauss = cv2.GaussianBlur(img, (5,5), 1.5)
cv2.imshow('Original', img)
cv2.imshow('Mean Blur', blur_mean)
cv2.imshow('Median Blur', blur_median)
cv2.imshow('Gaussian Blur', blur_gauss)
cv2.waitKey(0)
cv2.destroyAllWindows()Kiedy co stosować:
medianBlurdla zakłóceń impulsowych - bardzo skuteczny w usuwaniu pojedynczych pikseli szumu bez rozmywania krawędzi.GaussianBlurjako dobry kompromis do przedprzetwarzania (np. przed Otsu) - wygładza szum.blur(średnia) do bardzo prostych przypadków - ale może rozmywać ważne szczegóły, więc stosuj ostrożnie.
5. Transformacje geometryczne (translacja, obrót, skalowanie)
Podstawowe transformacje można wykonać za pomocą
cv2.warpAffine (2x3 macierz) lub
cv2.warpPerspective (3x3 macierz dla perspektywy).
Wybrane transformacje:
Translacja: przesunięcie obrazu o określony wektor (tx, ty).
Obrót: obrót obrazu o określony kąt wokół punktu (domyślnie środek).
Skalowanie: zmiana rozmiaru obrazu przez określony współczynnik (fx, fy).
Transformacje afiniczne i perspektywiczne: bardziej złożone transformacje, które mogą zawierać kombinację powyższych oraz dodatkowe efekty (np. zniekształcenia).
Przykłady:
import cv2
import numpy as np
img = cv2.imread('image.png')
h, w = img.shape[:2]
# Translacja
M = np.float32([[1, 0, 50], [0, 1, 30]]) # przesunięcie o (50,30)
translated = cv2.warpAffine(img, M, (w, h))
# Obrót wokół środka
center = (w//2, h//2)
angle = 30
scale = 1.0
R = cv2.getRotationMatrix2D(center, angle, scale)
rotated = cv2.warpAffine(img, R, (w, h))
cv2.imshow('Translated', translated)
cv2.imshow('Rotated', rotated)
cv2.waitKey(0)
cv2.destroyAllWindows()Uwaga: warpAffine przyjmuje 2x3 macierz transformacji
afinicznej.
6. Transformacje afiniczne i perspektywiczne
Afiniczne mapowanie zachowuje proste linie i równoległość kierunków; perspektywiczne pozwala na odwzorowanie czworokątów z perspektywą. Transformacje afiniczne są bardzo ważne do korekcji położenia obiektów, podczas gdy perspektywiczne są kluczowe przy korekcji zniekształceń wynikających z kąta widzenia.
Afiniczne (3 punkty):
import cv2
import numpy as np
img = cv2.imread('image.jpg')
h, w = img.shape[:2]
pts1 = np.float32([[50,50], [200,50], [50,200]])
pts2 = np.float32([[10,100], [200,50], [100,250]])
M_affine = cv2.getAffineTransform(pts1, pts2)
img_affine = cv2.warpAffine(img, M_affine, (w, h))
cv2.imshow('Affine Transform', img_affine)
cv2.waitKey(0)
cv2.destroyAllWindows()Perspektywiczne (4 punkty):
import cv2
import numpy as np
img = cv2.imread('image.jpg')
h, w = img.shape[:2]
src = np.float32([[0,0], [w-1,0], [w-1,h-1], [0,h-1]])
dst = np.float32([[300,300], [w-60,20], [w-200,h-400], [50,h-30]])
M_persp = cv2.getPerspectiveTransform(src, dst)
img_persp = cv2.warpPerspective(img, M_persp, (w, h))
cv2.imshow('Perspective Transform', img_persp)
cv2.waitKey(0)
cv2.destroyAllWindows()Wskazówki:
Dla afinicznych transformacji wystarczą 3 niekolinearne punkty źródłowe i docelowe.
Dla transformacji perspektywicznej wymagane są 4 punkty; dobrze sprawdź kolejność punktów (np. lewy-górny, prawy-górny, prawy-dolny, lewy-dolny).
7. Zadania do samodzielnego wykonania
Każde zadanie wykonaj w osobnym pliku Python (zad1.py,
zad2.py, …).
💥 Zadanie 1 - Progowanie pojedynczego kanału 💥
Wczytaj dowolny obraz kolorowy.
Przekonwertuj go do przestrzeni HSV.
Wydziel kanał nasycenia (S) i zastosuj progowanie na tym kanale.
Przekonwertuj wynik z powrotem do przestrzeni BGR i wyświetl.
💥 Zadanie 2 - Progowanie adaptacyjne 💥
Wczytaj obraz szarości z Księżyca.
Napisz program, który pozwala na interaktywne dostosowanie parametrów progowania adaptacyjnego (metoda, rozmiar bloku, stała C) i obserwuj efekty na obrazie. Wykorzystaj suwaki OpenCV do zmiany parametrów w czasie rzeczywistym.
Wyświetl efekt działania programu.
💥 Zadanie 3 - Operacje morfologiczne 💥
Bazując na kodzie dla przekształcenia perspektywicznego oraz przykładzie obsługi myszki, napisz program, który:
Wczytuje udostępniony obraz drogi,
Pozwala użytkownikowi kliknąć 4 punkty na obrazie, które będą służyć jako źródłowe punkty transformacji.
Wykonuje przekształcenie perspektywiczne w celu “wyprostowania” jej - tak, aby droga była widoczna z góry (bird’s-eye view). Pamiętaj o odpowiedniej kolejności punktów np. lewy-górny, prawy-górny, prawy-dolny, lewy-dolny.